﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ODM_Code.Code
{
    public class ConstraintsCode
    {
        private List<Constraint> constraintList;
        private int runningRowID;
        public static int fireCount;
        public static int periodCount;
        private string dbfPath;
        private string inputPath;
        private string alternativePath;

        public ConstraintsCode(string dbfPath, string inputPath, string alternativePath)
        {
            constraintList = new List<Constraint>();
            runningRowID = 9999;
            this.dbfPath = dbfPath;
            this.inputPath = inputPath;
            this.alternativePath = alternativePath;

            fireCount = this.CountFires();
            periodCount = this.CountPeriods();
        }

        public List<Constraint> getConstraintList()
        {
            constraintList.Sort();
            return constraintList;
        }

        public List<string[]> getConstraintRows()
        {
            List<string[]> rows = new List<string[]>();
            foreach (Constraint c in constraintList)
            {
                rows.Add(c.elems());
            }
            return rows;
        }

        private int CountFires()
        {
            string[] files = Directory.GetFiles(alternativePath, "inputfile*.asc");
            return files.Length;
        }

        private int CountPeriods()
        {
            string scenariosfile = alternativePath + "\\Scenarios.txt";
            int perCount = 0;
            if (File.Exists(scenariosfile))
            {
                StreamReader sr = new StreamReader(scenariosfile);
                string line = sr.ReadLine();
                line = sr.ReadLine();
                string[] spl = line.Split('\t');
                perCount = int.Parse(spl[3]);
                sr.Close();
            }
            return perCount;
        }

        public bool ParseConstraintFile()
        {
            string constraintsfile = alternativePath + "\\Constraints.txt";
            if (File.Exists(constraintsfile))
            {
                StreamReader sr = new StreamReader(constraintsfile);
                string line = sr.ReadLine();
                while (!sr.EndOfStream)
                {
                    line = sr.ReadLine();
                    string[] spl = line.Split('\t');
                    Constraint con = new Constraint();
                    con.rowId = int.Parse(spl[0]);
                    con.name = spl[1];
                    con.lowLimit = float.Parse(spl[2]);
                    con.uppLimit = float.Parse(spl[3]);
                    con.period = int.Parse(spl[4]);
                    con.weight = float.Parse(spl[5]);
                    con.rel = spl[6];

                    constraintList.Add(con);
                    runningRowID -= 2;
                }

                sr.Close();
                return true;
            }
            else
            {
                this.ConstraintsAutoGenerator();
                return false;
            }
        }

        public void WriteConstraints()
        {
            StreamWriter sw = new StreamWriter(alternativePath + "\\Constraints.txt");
            sw.WriteLine("INT_ROW_ID\tNAME\tL_LIMIT\tU_LIMIT\tPERIOD\tWEIGHT\tREL");

            for (int i = 0; i < constraintList.Count; i++)
            {
                string line = constraintList[i].ToString();
                sw.WriteLine(line);
            }

            sw.Close();
        }

        private void ConstraintsAutoGenerator()
        {
            int fireCount = ConstraintsCode.fireCount;
            int perCount = ConstraintsCode.periodCount;
            int totCount = fireCount * perCount;
            for (int i = 1; i <= totCount; i++)
            {
                Constraint con = new Constraint();
                con.rowId = runningRowID;
                if (i <= 3)
                {
                    con.name = "AC_TR" + i.ToString();
                    con.period = i;
                }
                else
                {
                    con.name = "AC_OT" + (i - 3).ToString();
                    con.period = i - 3;
                }
                con.lowLimit = 500f;
                con.uppLimit = 2500f;
                con.weight = 0.33f;
                constraintList.Add(con);
                runningRowID -= 2;
            }
        }

        private void SortConList(List<Constraint> conList, Comparer<Constraint> c)
        {
            if (conList.Count < 2) { return; }
            SortConListStep(conList, c, 0, conList.Count - 1);
        }

        private void SortConListStep(List<Constraint> conList, Comparer<Constraint> c, int leftBound, int rightBound)
        {
            if (leftBound >= rightBound) { return; }
            Constraint temp;
            Constraint pivot = conList[rightBound];

            int leftInd = leftBound;
            int rightInd = rightBound - 1;

            while (leftInd <= rightInd)
            {
                while ((leftInd <= rightInd) && (c.Compare(conList[leftInd], pivot) <= 0))
                {
                    leftInd++;
                }
                while ((rightInd >= leftInd) && (c.Compare(conList[rightInd], pivot) >= 0))
                {
                    rightInd--;
                }
                if (leftInd < rightInd)
                {
                    temp = conList[rightInd];
                    conList[rightInd] = conList[leftInd];
                    conList[leftInd] = temp;
                }
            }

            temp = conList[rightBound];
            conList[rightBound] = conList[leftInd];
            conList[leftInd] = temp;
            SortConListStep(conList, c, leftBound, leftInd - 1);
            SortConListStep(conList, c, leftInd + 1, rightBound);
        }

        public void AddConstraint(object[] row)
        {
            Constraint con = new Constraint(row);
            constraintList.Add(con);
        }

        public void RemoveConstraint(int rowId)
        {
        }

        public void UpdateConstraint(int colId, int rowId, object newVal)
        {
            constraintList[rowId].UpdateField(colId, newVal);
        }

        public class Constraint : IComparable<Constraint>
        {
            public enum conItems { INT_ROW_ID, NAME, L_LIMIT, U_LIMIT, PERIOD, WEIGHT, REL } ;

            public int rowId;
            public string name;
            public float lowLimit;
            public float uppLimit;
            public int period;
            public float weight;
            public string rel;

            public Constraint()
            {
            }

            public Constraint(object[] row)
            {
                this.rowId = Convert.ToInt16(row[0]);
                this.name = Convert.ToString(row[1]);
                this.lowLimit = Convert.ToSingle(row[2]);
                this.uppLimit = Convert.ToSingle(row[3]);
                this.period = Convert.ToInt16(row[4]);
                this.weight = Convert.ToSingle(row[5]);
            }

            public void UpdateField(int id, object val)
            {
                switch (id)
                {
                    case 0:
                        this.rowId = Convert.ToInt16(val);
                        break;
                    case 1:
                        this.name = Convert.ToString(val);
                        break;
                    case 2:
                        this.lowLimit = Convert.ToSingle(val);
                        break;
                    case 3:
                        this.uppLimit = Convert.ToSingle(val);
                        break;
                    case 4:
                        this.period = Convert.ToInt16(val);
                        break;
                    case 5:
                        this.weight = Convert.ToSingle(val);
                        break;
                    case 6:
                        this.rel = Convert.ToString(val);
                        break;
                }
            }

            public string[] elems()
            {
                string[] retAry = {
                                      rowId.ToString(),
                                      name,
                                      lowLimit.ToString(),
                                      uppLimit.ToString(),
                                      period.ToString(),
                                      weight.ToString()
                                  };
                return retAry;
            }
            public override string ToString()
            {
                string retString = "";
                retString += rowId.ToString();
                retString += "\t" + name;
                retString += "\t" + lowLimit.ToString();
                retString += "\t" + uppLimit.ToString();
                retString += "\t" + period.ToString();
                retString += "\t" + weight.ToString();
                retString += "\t" + rel;
                return retString;
            }
            public int CompareTo(Constraint that)
            {
                if (this.rowId > that.rowId)
                {
                    return -1;
                }
                else if (this.rowId < that.rowId)
                {
                    return 1;
                }
                else
                {
                    return 0;
                }

                //return this.rowId.CompareTo(that.rowId);
            }
        }
    }
}
